#pragma once

#include "primitive.h"

#include <vector>

namespace kp {

	class Accel : public Primitive {
	public:
		Accel() {}

		virtual bool Intersect(const Ray& r, Intersection& rec) const = 0;

		virtual Ref<Material> getMaterial() const override { return nullptr; }

		virtual void ComputeScatteringFunctions(Intersection* si,
			TransportMode mode, bool allowMultipleLobes) const override {}

		virtual BBox3f getBound() const = 0;

		virtual size_t size() const = 0;

	private:

	};

	class ListAccel : public Accel {
	public:
		virtual bool Intersect(const Ray& r, Intersection& rec) const override {
			bool isHited = false;
			for (auto& pr : primitives) {
				if (pr->Intersect(r, rec)) {
					isHited = true;
				}
			}
			return isHited;
		}

		void push_back(const Ref<Primitive>& primitive) {
			primitives.push_back(primitive);
		}

		void push_back(const std::vector<Ref<Primitive>>& primitives) {
			for (auto& pri : primitives) {
				this->primitives.push_back(pri);
			}
		}

		virtual BBox3f getBound() const override {
			return BBox3f();
		}

		virtual size_t size() const override { return primitives.size(); }

		std::vector<Ref<Primitive>> primitives;

	};

	struct BVHBuildNode;
	struct BVHPrimitiveInfo;

	struct LinearBVHNode {
		BBox3f bbox;
		union {
			uint32_t primitiveOffset;
			uint32_t secondChildOffset;
		};
		uint8_t nPrimitives;
		uint8_t axis;
		uint8_t pad[2];
	};

	class BVHAccel : public Accel {
	public:

		enum class SplitMethod {
			SAH,
			HLBVH,
			Middle,
			EqualCounts
		};

		virtual bool Intersect(const Ray& r, Intersection& rec) const override;

		virtual BBox3f getBound() const override;

		virtual size_t size() const override { return primitives.size(); }

		BVHAccel(const std::vector<Ref<Primitive>>& primitives, int maxPrimsInNode,
			SplitMethod splitmethod = SplitMethod::SAH);

	private:
		BVHBuildNode* recursiveBuild(std::vector<BVHPrimitiveInfo>& primitiveInfo,
			int start, int end, int& totalNodes, std::vector<Ref<Primitive>>& orderedPrims);

		int flattenTree(BVHBuildNode* node, int& offset);

	private:
		std::vector<Ref<Primitive>> primitives;
		const int maxPrimsInNode;
		SplitMethod spm;
		std::vector<LinearBVHNode> lbnodes;
	};

	class OctAccel : public Accel {
	public:
		virtual bool Intersect(const Ray& r, Intersection& rec) const override {
			return false;
		}

		virtual BBox3f getBound() const override {
			return BBox3f();
		}

		virtual size_t size() const override { return 0; }

	};

	class KDAccel : public Accel {
	public:
		virtual bool Intersect(const Ray& r, Intersection& rec) const override {
			return false;
		}

		virtual BBox3f getBound() const override {
			return BBox3f();
		}

		virtual size_t size() const override { return 0; }

	};

}
